package com.navinfo.tripanalysis.offline.service.impl;

import com.navinfo.tripanalysis.common.arithmetic.*;
import com.navinfo.tripanalysis.common.arithmetic.common.*;
import com.navinfo.tripanalysis.common.arithmetic.custom.VarFuelChange;
import com.navinfo.tripanalysis.common.arithmetic.enums.EventType;
import com.navinfo.tripanalysis.common.arithmetic.manager.ArithmeticManager;
import com.navinfo.tripanalysis.common.config.ConfigMap;
import com.navinfo.tripanalysis.common.pojo.*;
import com.navinfo.tripanalysis.common.util.ArithmeticUtils;
import com.navinfo.tripanalysis.common.util.CommonUtils;
import com.navinfo.tripanalysis.common.util.DateUtils;
import com.navinfo.tripanalysis.offline.pojo.AlgorithmChainCommand;
import com.navinfo.tripanalysis.offline.service.AlgorithmChainService;
import com.navinfo.tripanalysis.offline.util.TripStatisticUtils;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.apache.spark.broadcast.Broadcast;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * 抽象的算法链服务
 * @author 沈东生
 */
@Data
public abstract class AbstractAlgorithmChainService implements AlgorithmChainService {
    protected final Logger logger = LoggerFactory.getLogger(getClass());
    /**
     * 最小速度阀值
     */
    private int speedMin;
    /**
     * 最大速度阀值
     */
    private int speedMax;
    /**
     * 引擎最小转速阀值
     */
    private int rotationMin;
    /**
     * 引擎最大转速阀值
     */
    private int rotationMax;
    /**
     * 行程结束发动机转数低转时长
     */
    private int tripEndRotationTime;
    /**
     * 跨天的时间阀值，单位：秒<br/>
     * 距下一天多少秒内算行程未结束，算跨天行程。<br/>
     * 比如:设为30秒，则最后一个点的 gps时间为23:59:30到23:59:59 都算跨天行程
     */
    private long crossDayThresholdS;

    /**
     *  驾驶行为分析0F37管理器
     */
    protected ArithmeticManager arithmeticManager0F37;
    /**
     *  驾驶行为分析0200管理器
     */
    protected ArithmeticManager arithmeticManager0200;
    /**
     * 统计的来源，1：0F37 ,2：0200
     */
    private int statisticSource;
    /**
     * 事件的来源，1：0F37 ,2：0200
     */
    private int eventSource;
    /**
     * 当前使用的算法管理器
     */
    private ArithmeticManager arithmeticManager;

    protected AbstractAlgorithmChainService(Map algorithmConfig){
        //初始化算法配置参数
        ConfigMap.init(algorithmConfig);
        this.setRotationMax(Integer.parseInt(ConfigMap.getString("rotation_max")));
        this.setRotationMin(Integer.parseInt(ConfigMap.getString("rotation_min")));
        this.setSpeedMax(Integer.parseInt(ConfigMap.getString("speed_max")));
        this.setSpeedMin(Integer.parseInt(ConfigMap.getString("speed_min")));
        this.setTripEndRotationTime(Integer.parseInt(ConfigMap.getString("trip_end_rotation_time")));
        this.setCrossDayThresholdS(Integer.parseInt(ConfigMap.getString("trip_crossDay_threshold_s")));

        //基础0F37算法链
        arithmeticManager0F37 = new ArithmeticManager()
            //需先执行的任务
            .addArithmetic(new Mileage())
            .addArithmetic(new Gear())
            .addArithmetic(new Brake())
            .addArithmetic(new IdleParking())
            .addArithmetic(new DrivingSlopetest())

            //无顺序的任务
            .addArithmetic(new BrakeHand())
            .addArithmetic(new BrakeNormal())
            .addArithmetic(new BrakeHighSpeed())
            .addArithmetic(new CarStop())
            .addArithmetic(new CarStart())
            .addArithmetic(new CarIgnite())
            .addArithmetic(new CarFlameout())
            .addArithmetic(new CrossCity())
            .addArithmetic(new ClutchSwitch())
            .addArithmetic(new CruiseControl())
            .addArithmetic(new CoastInGear())
            .addArithmetic(new CoastNeutralGear())
            .addArithmetic(new DrivingEco())
            .addArithmetic(new DrivingCity())
            .addArithmetic(new DrivingFatigue())
            .addArithmetic(new FuelChange())
            .addArithmetic(new GasSharp())
            .addArithmetic(new GasSteady())
            .addArithmetic(new ParkingAccelerator())
            .addArithmetic(new RotationLow())
            .addArithmetic(new RotationOver())
            .addArithmetic(new RangeClock())
            .addArithmetic(new RangeSpeedTest())
            .addArithmetic(new RangeTorque())
            .addArithmetic(new RangeAltitude())
            .addArithmetic(new RangeRotation())
            .addArithmetic(new RangeWaterTemp())
            .addArithmetic(new RangeIntakeAirTemp())
            .addArithmetic(new RangeGasOpenPercentTest())
            .addArithmetic(new RangeIntakeAirPressure())
            .addArithmetic(new SpeedOver())
            .addArithmetic(new SpeedOver120())
            .addArithmetic(new SharpTurning())
            .addArithmetic(new SharpAcceleration())
            .addArithmetic(new SharpDeceleration())
            .addArithmetic(new TripPower())
            .addArithmetic(new VehicleEstimatedLoad());

        //基础0200算法链
        arithmeticManager0200 = new ArithmeticManager()
            .addArithmetic(new Mileage())
            .addArithmetic(new IdleParking())
            .addArithmetic(new BrakeHand())
            .addArithmetic(new CarStop())
            .addArithmetic(new CarIgnite())
            .addArithmetic(new CarFlameout())
            .addArithmetic(new CrossCity())
            .addArithmetic(new CruiseControl())
            .addArithmetic(new DrivingFatigue())
            .addArithmetic(new FuelChange())
            .addArithmetic(new RotationLow())
            .addArithmetic(new RotationOver())
            .addArithmetic(new RangeClock())
            .addArithmetic(new RangeSpeed())
            .addArithmetic(new RangeTorque())
            .addArithmetic(new RangeAltitude())
            .addArithmetic(new RangeRotation())
            .addArithmetic(new RangeWaterTemp())
            .addArithmetic(new RangeIntakeAirTemp())
            .addArithmetic(new RangeGasOpenPercent())
            .addArithmetic(new RangeIntakeAirPressure())
            .addArithmetic(new ParkingAccelerator())
            .addArithmetic(new SpeedOver())
            .addArithmetic(new SpeedOver120())
            .addArithmetic(new TripPower())
            .addArithmetic(new NumberBy0200());
       /* List<Class<? extends IArithmetic>> excludes = new ArrayList<>();
        excludes.add(SharpAcceleration.class);//急加速
        excludes.add(SharpDeceleration.class); //急减速
        excludes.add(GasSharp.class); //急踩油门
        excludes.add(SharpTurning.class);//急转弯
        excludes.add(CoastNeutralGear.class);//空挡滑行
        excludes.add(Brake.class); //制动相关算法
        excludes.add(BrakeNormal.class); //制动相关算法
        excludes.add(BrakeHighSpeed.class); //制动相关算法
        excludes.add(CoastInGear.class); //在挡滑行
        excludes.add(CarStart.class);//启步,冷车启步，停车
        excludes.add(GasSteady.class);//油门稳定性
        excludes.add(VehicleEstimatedLoad.class); //整车估算载荷
        excludes.add(DrivingCity.class);//城市行驶 非城市
        excludes.add(DrivingEco.class); //经济行驶
        excludes.add(DrivingSlope.class);//坡路行驶
        excludes.add(Gear.class);//挡位算法
        excludes.add(ClutchSwitch.class);//离合开关

        excludes.add(SharpAcceleration_DF.class);//东风急加速
        excludes.add(SharpDeceleration_DF.class); //东风急减速
        excludes.add(SharpTurning_DF.class);//东风急转弯
        excludes.add(DrivingCold_DF.class);//东风冷车运行
        excludes.add(GasLargeAndFull_DF.class);//东风大油门和全油门
        excludes.add(CoastNeutralGear_DF.class);//东风空挡滑行
        excludes.add(CoastFlameout_DF.class);//东风熄火滑行
        excludes.add(SharpTurning_DFN.class);//东风急转弯
        excludes.add(EmergencyStop_DFN.class);//东风紧急停车*/
    }

    @Override
    public void execute(AlgorithmChainCommand command) {
        List<Point> points0f37 = command.getPoints0f37();
        List<Point0200> points0200 = command.getPoints0200();

        //0F37算法链
        if (CommonUtils.isCollectionNotEmpty(points0f37)) {
            arithmeticManager = arithmeticManager0F37;
            statisticSource = OuterStatisticData.S_TYPE_0F37;
            eventSource = OuterEventData.S_TYPE_0F37;

            //0f37与0200点进行混流后输出
            if (CommonUtils.isCollectionNotEmpty(points0200)) {
                points0f37 = new ArrayList<>(points0f37.size() + points0200.size());
                points0f37.addAll(points0200);
                points0f37.addAll(command.getPoints0f37());
                points0f37.sort(Point.COMPARATOR);
            }

            int pointSize = points0f37.size();

            //最后一个0f37点
            int last0f37Index = -1;
            for (int i=pointSize-1; i>=0; i--) {
                if (ProtocolEnum.REALTIME.equals(points0f37.get(i).getProtocol() )) {
                    last0f37Index = i;
                    break;
                }
            }
            if (last0f37Index == -1) {
                logger.error("未发现0f37点，不处理，{}", command.getTid());
                return;
            }

            DrivingData drivingData = command.getDrivingData();
            for (int i=0; i<pointSize; i++) {
                Point point = points0f37.get(i);
                //若原始点是0F37
                switch (point.getProtocol()){
                    //若原始点是0F37
                    case REALTIME:
                        if (i != last0f37Index) {
                            dealPoint(command, drivingData, point);
                        }else{
                            //如果是最后一个0f37点
                            dealLastPoint(command, drivingData, point);
                        }
                        break;
                    //若原始点是0200，复制0200的数据到算法的公共变量中
                    case LOCATION:
                        TripStatisticUtils.assign0200Data(drivingData ,(Point0200) point);
                        break;
                    case ENGINE:
                        TripStatisticUtils.assignEngineData(drivingData, (PointEngine) point);
                    default:
                        break;
                }
            }

            if(command.isHandleCrossDay()) {
                handleCrossDay(command.getOuterData().getOutStatistics(), drivingData);
            }
        }
        else{
            //0200算法链
            if (CommonUtils.isCollectionNotEmpty(points0200)) {
                arithmeticManager = arithmeticManager0200;
                statisticSource = OuterStatisticData.S_TYPE_0200;
                eventSource = OuterEventData.S_TYPE_0200;

                DrivingData drivingData = command.getDrivingData();
                int size = points0200.size();
                for (int i = 0; i<size; i++) {
                    Point point = points0200.get(i);
                    if (i != size-1) {
                        dealPoint(command, drivingData, point);
                    } else {
                        dealLastPoint(command, drivingData, point);
                    }
                }

                if(command.isHandleCrossDay()) {
                    handleCrossDay(command.getOuterData().getOutStatistics(), drivingData);
                }
            } else {
                logger.error("原始点数据为空！");
            }
        }

        command.getOuterData().setDrivingData(command.getDrivingData());
    }

    /**
     * 处理一个位置点数据
     * @param command      算法链执行的命令对象
     * @param drivingData 中间变量
     * @param point        点信息
     */
    protected void dealPoint(AlgorithmChainCommand command, DrivingData drivingData, Point point) {
        long tid = command.getTid();
        CommonData commonData = drivingData.getCommonData();

        long gpsTime = point.getGpsTime();
        long lastGpsTime = commonData.getLastGpsTime();
        int rotation = point.getRotation();
        int speed = (int) point.getSpeedKmh();

        //数据不合法跳过
        if (!checkData(tid, lastGpsTime, gpsTime, rotation, speed)) {
            return;
        }

        //如果中间变量中没有行程数据进行初始化
        if (drivingData.getTripInfo() == null || drivingData.getTripInfo().getStartTime()<=0) {
            startTrip(tid, drivingData, point);
        } else {
            commonData.setTripFirstNode(false);
        }

        //中间变量的公共缓存赋值
        TripStatisticUtils.assign(point, drivingData);

        //调用算法链计算相应指标（临时数据将发生变化）
        arithmeticManager.setDrivingData(drivingData).calculate();

        //为下次计算提供相关数据
        commonData.setLastGpsTime(gpsTime);
        commonData.setLastRotation(rotation);
        commonData.setLastSpeedMs(commonData.getSpeedMs());
        commonData.setLastAcceleratorPosition(commonData.getAcceleratorPosition());

        //将中间变量的事件数据放到计算结果的事件数据中
        outPutEvents(drivingData, command, tid);
    }

    /**
     * 处理最后一个位置点数据
     * @param command   算法链的模型对象
     * @param drivingData 算法链的中间缓存
     * @param point 位置点
     */
    protected void dealLastPoint(AlgorithmChainCommand command, DrivingData drivingData, Point point) {
        //如果中间变量中没有行程数据进行初始化
        if (drivingData.getTripInfo()==null || drivingData.getTripInfo().getStartTime() == 0) {
            startTrip(command.getTid(), drivingData, point);
        }

        CommonData commonData = drivingData.getCommonData();
        commonData.setTripLastNode(true);
        commonData.setTripFirstNode(false);

        long curGpsTime = point.getGpsTime();

        //与前一个点的gpstime不同
        if (curGpsTime - commonData.getGpsTime() > 0) {
            TripStatisticUtils.assign(point, drivingData);
        }

        //调用算法链计算相应指标（临时数据将发生变化）
        arithmeticManager.setDrivingData(drivingData).calculate();

        //行程结束处理
        endTrip(command, drivingData, curGpsTime);
    }

    /**
     * 行程开始处理，中间变量放入行程数据
     * @param tid         通信号
     * @param drivingData 中间变量
     * @param point       0F37轨迹点
     */
    private void startTrip(Long tid, DrivingData drivingData, Point point) {
        logger.info("===行程开始! 通信号：{} ===", tid);

        //处理CommonData
        CommonData commonData = drivingData.getCommonData();
        commonData.clear();
        commonData.setTid(tid);
        commonData.setTripFirstNode(true);

        TripInfo tripInfo = new TripInfo();
        tripInfo.setStartTime(point.getGpsTime());
        tripInfo.setStartHeight((int) point.getHeight());
        tripInfo.setStartLongitude(point.getLongitude());
        tripInfo.setStartLatitude(point.getLatitude());
        tripInfo.setStartDirection(point.getDirection());
        drivingData.setTripInfo(tripInfo);
    }

    /**
     * 行程结束处理
     * @param command     算法链执行的命令对象
     * @param drivingData 中间变量
     * @param curGpsTime 最后一个点的gps时间
     */
    private void endTrip(AlgorithmChainCommand command, DrivingData drivingData, long curGpsTime) {
        TripInfo tripInfo = drivingData.getTripInfo();
        if (tripInfo == null) {
            return;
        }

        long tid = command.getTid();
        CommonData commonData = drivingData.getCommonData();
        tripInfo.setEndTime(commonData.getGpsTime());
        tripInfo.setEndHeight(commonData.getHeight());
        tripInfo.setEndLongitude(commonData.getLongitude());
        tripInfo.setEndLatitude(commonData.getLatitude());
        tripInfo.setEndDirection(commonData.getGpsDirection());

        //收集并补充行程结束信息
        OuterStatisticData statisticData = collectTripData(drivingData, tid);

        //新增发动机数据相关------------------------------------------------------------------------------------------------
        //是国六车辆，并且vin不为空，并且本次行程有发动机数据，那么汇总发动机数据，否则就设置一个空对象，落盘前会将空对象剔除
        Cn6BasicDataInfo cn6BasicDataInfo = drivingData.getCn6BasicDataInfo();
        EngineAggregatedData engineAggregatedData = drivingData.getEngineAggregatedData();
        if (cn6BasicDataInfo != null && StringUtils.isNotBlank(cn6BasicDataInfo.getVin()) && engineAggregatedData.getEngineDataPoints() > 0) {
            //收集行程发动机数据汇总信息
            engineAggregatedData.setTid(tid);
            //设置行程相关的数据
            engineAggregatedData.setRouteStartTime(tripInfo.getStartTime());
            engineAggregatedData.setRouteEndTime(tripInfo.getEndTime());
            engineAggregatedData.setRouteDuration((int) (tripInfo.getEndTime() - tripInfo.getStartTime()));
            //设置车辆相关的数据
            engineAggregatedData.setVin(cn6BasicDataInfo.getVin());
            engineAggregatedData.setEmissionStandard(cn6BasicDataInfo.getEmissionValue());
//            engineAggregatedData.setCarCategory(cn6BasicDataInfo.getCityType());
            engineAggregatedData.setEngineType(cn6BasicDataInfo.getEngineModelName());
            engineAggregatedData.setCarType(cn6BasicDataInfo.getNoticeModel());
            //设置缺项和异常的指标名称列表
            int engineDataMissTerms = engineAggregatedData.getExistEngineDataMiss();
            int engineDataAbnormalTerms = engineAggregatedData.getExistEngineDataAbnormal();
            int newEngineDataMissTerms = 0;
            int newEngineDataAbnormalTerms = 0;
            StringBuilder missTermsBuilder = new StringBuilder();
            StringBuilder abnormalTermsBuilder = new StringBuilder();
            for (EngineDataTerm term : EngineDataTerm.values()) {
                if ((engineDataMissTerms & (1 << term.ordinal())) == Math.pow(2, term.ordinal()) &&
                        isMissTerm(engineAggregatedData, term.getMissGetterName())) {
                    String name = term.getName();
                    missTermsBuilder.append(name).append(",");
                    newEngineDataMissTerms = newEngineDataMissTerms | (1 << term.ordinal());
                }
                if ((engineDataAbnormalTerms & (1 << term.ordinal())) == Math.pow(2, term.ordinal()) &&
                        isAbnmTerm(engineAggregatedData, term.getAbnmGetterName())) {
                    String name = term.getName();
                    abnormalTermsBuilder.append(name).append(",");
                    newEngineDataAbnormalTerms = newEngineDataAbnormalTerms | (1 << term.ordinal());
                }
            }
            engineAggregatedData.setExistEngineDataMiss(newEngineDataMissTerms);
            engineAggregatedData.setExistEngineDataAbnormal(newEngineDataAbnormalTerms);
            //赋值缺项的指标名称列表
            if (missTermsBuilder.length() > 0) {
                String missTermNames = missTermsBuilder.deleteCharAt(missTermsBuilder.length() - 1).toString();
                engineAggregatedData.setEngineDataMissTerms(missTermNames);
            }
            //赋值异常的指标名称列表
            if (abnormalTermsBuilder.length() > 0) {
                String abnormalTermNames = abnormalTermsBuilder.deleteCharAt(abnormalTermsBuilder.length() - 1).toString();
                engineAggregatedData.setEngineDataAbnmTerms(abnormalTermNames);
            }
        } else {
            //不是国六车辆
            engineAggregatedData = null;
        }
        //添加发动机指标汇总数据
        command.getOuterData().getOutEngineStatistics().add(engineAggregatedData);
        //新增发动机数据相关------------------------------------------------------------------------------------------------

        //获取事件列表
        List<OuterEventData> eventList = Optional.ofNullable(drivingData.getOuterEvent()).orElse(new ArrayList<>());

        boolean crossDay = false;
        if (command.isHandleCrossDay()) {
            //行程是否跨天的判断：最后一个位置点的时间到第二天0点的时间间隔<阀值，为跨天行程；
            crossDay = ((DateUtils.addDays(command.getDay(), 1).getTime()-curGpsTime) <= crossDayThresholdS*1000);
        }

        if (!crossDay) {
            statisticData.setCrossDayFlag(0);

            //未跨天的行程，强制结束未结束事件
            Map<EventType, OuterEventData> eventDataMap = drivingData.getEventData();
            for (Map.Entry<EventType, OuterEventData> entry : eventDataMap.entrySet()) {
                OuterEventData event = entry.getValue();
                event.setStopGpsDirection(tripInfo.getEndDirection());
                event.setStopGpsTime(tripInfo.getEndTime());
                event.setStopHeight(tripInfo.getEndHeight());
                event.setStopLatitude(tripInfo.getEndLatitude());
                event.setStopLongitude(tripInfo.getEndLongitude());
                event.setStopSpeed(commonData.getSpeedKmh());
                event.setDuration(tripInfo.getEndTime() - event.getStartGpsTime());
                eventList.add(event);
            }
            drivingData.setEventData(new HashMap<>(16));
        } else {
            //行程跨天标识
            statisticData.setCrossDayFlag(2);
        }

        //赋值计算结果的行程数据
        command.getOuterData().getOutStatistics().add(statisticData);
        logger.info("===已将结束的行程放入返回列表中! 通信号：{} ===", tid);

        //赋值计算结果的事件数据
        outPutEvents(drivingData, command, tid);

        //行程结束，记录现在的油液情况用于下次行程开始时计算加油漏油
        VarFuelChange varFuelChange = new VarFuelChange();
        Double oilPer = commonData.getOilPercentage();
        if (oilPer != null) {
            varFuelChange.setLastTripOilPer(oilPer);
        }
        Double totalOil = commonData.getTotalOil();
        if (totalOil != null) {
            varFuelChange.setLastTotalOil(totalOil);
        }
        varFuelChange.setLastGpsTime(commonData.getGpsTime());

        //重置中间变量的值
        drivingData.clear();

        drivingData.getStatisticData().put(FuelChange.class.getSimpleName(), varFuelChange);

        //行程结束，初始化停车通电相关参数，用于下次行程开始时计算停车通电时长等信息
        //只适用于流计算，离线计算不包含行程外的位置点
        //VarParkingPowerOn varParkingPowerOn = new VarParkingPowerOn();
        //varParkingPowerOn.setParkingTime(tripInfo.getEndTime());
        //drivingData.getOuterStatistic().put(ParkingPowerOn.class.getSimpleName(), varParkingPowerOn);

        logger.info("===行程结束! 通信号：{} ===", tid);
    }

    /**
     * 处理跨天标志，当前的业务逻辑：一个行程是只允许跨一天。<br/>
     * 第一次跨天：昨天最后一个Trip的crossDay=0、DrivingData的crossDay=0，<br/>
     *             今天第一个Trip的crossDay=0<br/>
     *             今天最后一个Trip的crossDay=2、DrivingData的crossDay=2<br/>
     *
     * 昨天跨天：昨天最后一个Trip的crossDay=2、DrivingData的crossDay=2，<br/>
     *           今天第一个Trip的crossDay=1，<br/>
     *               如果今天就一个行程，最后一个Trip的crossDay=0、DrivingData的crossDay=0<br/>
     *               如果今天有多个行程，DrivingData的crossDay值和最后一个Trip的crossDay值相同<br/>
     * @param trips 行程统计的输出列表
     * @param drivingData 中间变量中存储的跨天标志是昨天的跨天标志
     */
    protected void handleCrossDay(List<OuterStatisticData> trips, DrivingData drivingData) {
        if (CommonUtils.isCollectionEmpty(trips)) {
            drivingData.setCrossDayFlag(0);
            return;
        }

        OuterStatisticData firstTrip = trips.get(0);
        OuterStatisticData lastTrip = trips.get(trips.size() - 1);

        //昨天最后一个行程的跨天标志
        int preStatus = drivingData.getCrossDayFlag();
        //今天最后一个行程的跨天标志
        int lastTripStatus = lastTrip.getCrossDayFlag();

       /* 第一次跨天：昨天最后一个Trip的crossDay=0、DrivingData的crossDay=0
        *             今天第一个Trip的crossDay=0<br/>
        *             今天最后一个Trip的crossDay=2、DrivingData的crossDay=2<br/>
        */
        if (preStatus == 0 ) {
            //今天第一个行程为0
            firstTrip.setCrossDayFlag(0);

            if (lastTripStatus == 2) {
                drivingData.setCrossDayFlag(2);

                //防止第一个行程和最后一个行程是同一个，重新赋值
                lastTrip.setCrossDayFlag(2);
            }
        }
        /*昨天跨天：昨天最后一个Trip的crossDay=2、DrivingData的crossDay=2，<br/>
         *          今天第一个Trip的crossDay=1，<br/>
         *               如果今天就一个行程，最后一个Trip的crossDay=0、DrivingData的crossDay=0<br/>
         *               如果今天有多个行程，DrivingData的crossDay值和最后一个Trip的crossDay值相同<br/>
         */
        else {
            //今天的第一条行程设置跨天标识
            firstTrip.setCrossDayFlag(1);

            //今天就一个行程
            if (firstTrip == lastTrip) {
                //强制最后一个点的跨天标志位0
                lastTrip.setCrossDayFlag(0);
                drivingData.setCrossDayFlag(0);
            }
            //今天有多个行程，DrivingData的crossDay值和最后一个Trip的crossDay值相同
            else{
                drivingData.setCrossDayFlag(lastTripStatus);
            }
        }
    }

    /**
     * 收集并补充行程结束信息
     * @param drivingData 中间变量
     * @param tid           通信号
     * @return 行程
     */
    protected OuterStatisticData collectTripData(DrivingData drivingData, long tid) {
        OuterStatisticData statisticData = new OuterStatisticData();
        statisticData.setStype(statisticSource);

        //循环调用算法统计结果
        arithmeticManager.setDrivingData(drivingData).collect(statisticData);

        //行程数据赋值
        TripInfo tripInfo = drivingData.getTripInfo();
        statisticData.setTid(tid);
        statisticData.setUid(tid);
        statisticData.setForceEndFlag(0);
        statisticData.setRouteStopFlag(1);
        statisticData.setDay(DateUtils.format(new Date(tripInfo.getStartTime()), DateUtils.DateFormat.YYYYMMDD));

        //使用行程的数据赋值，之后会在TripAnalysisApplication中使用0200数据精准
        statisticData.setRouteStartTime(tripInfo.getStartTime() / 1000);
        statisticData.setRouteStartLatitude(tripInfo.getStartLatitude());
        statisticData.setRouteStartLongitude(tripInfo.getStartLongitude());
        statisticData.setRouteStartHeight(tripInfo.getStartHeight());
        statisticData.setRouteEndTime(tripInfo.getEndTime() / 1000);
        statisticData.setRouteStartDirection(tripInfo.getStartDirection());
        statisticData.setRouteEndLongitude(tripInfo.getEndLongitude());
        statisticData.setRouteEndLatitude(tripInfo.getEndLatitude());
        statisticData.setRouteEndHeight(tripInfo.getEndHeight());
        statisticData.setRouteEndDirection(tripInfo.getEndDirection());
        statisticData.setTripDuration((int) (statisticData.getRouteEndTime() - statisticData.getRouteStartTime()));

        //设置行程的里程，油耗，后面还会在TripAnalysisApplication中精准
        statisticData.setTripFuel(statisticData.getRouteIntegralFuel());
        statisticData.setTripMileage(statisticData.getRouteIntegralMileage());

        //根据车辆的经纬度，补充对应的行程开始结束的区域数据，后面还会在TripAnalysisApplication中精准

        Broadcast<Map<Long, Integer>> tileLcBroadcast = drivingData.getTileLcBroadcast();
        if (tileLcBroadcast != null) {
            Map<Long, Integer> tileLcMapping = tileLcBroadcast.getValue();
            statisticData.setRouteStartLc(ArithmeticUtils.getLc(statisticData.getRouteStartLatitude(), statisticData.getRouteStartLongitude(), tileLcMapping));
            statisticData.setRouteEndLc(ArithmeticUtils.getLc(statisticData.getRouteEndLatitude(), statisticData.getRouteEndLongitude(), tileLcMapping));
        }

        return statisticData;
    }

    /**
     * 收集补充基础的事件相关数据
     * @param command 算法链的模型对象
     * @param tid   通信id
     * @param eventData 事件输出对象
     */
    protected void collectEventData(AlgorithmChainCommand command, long tid, OuterEventData eventData) {
        eventData.setTid(tid);
        eventData.setUid(tid);
        eventData.setStype(eventSource);
        eventData.setDay(DateUtils.format(command.getDay(), DateUtils.DateFormat.YYYYMMDD));
    }

    /**
     * 将中间变量的事件数据，放到计算结果的事件数据中
     * @param drivingData 算法链的缓存对象
     * @param command   算法链的模型对象
     * @param tid   通信id
     */
    private void outPutEvents(DrivingData drivingData, AlgorithmChainCommand command, long tid) {
        for (OuterEventData eventData : drivingData.getOuterEvent()) {
            collectEventData(command, tid, eventData);
            List<OuterEventData> outEvents = command.getOuterData().getOutEvents();
            if (outEvents != null) {
                outEvents.add(eventData);
            } else {
                logger.info("不输出事件数据{}", tid);
            }
        }

        //清除中间变量的事件数据
        drivingData.getOuterEvent().clear();
    }

    /**
     * 校验数据合法性
     * @param tid             通信号
     * @param lastGpsTime     上个点时间
     * @param curGpsTime 当前点时间
     * @param rotation        转速
     * @param speed           速度
     * @return 是否合法
     */
    private boolean checkData(long tid, long lastGpsTime, long curGpsTime, int rotation, int speed) {
        //本次gps时间小于等于上个点的gps时间，不予处理
        if (curGpsTime <= lastGpsTime) {
            logger.info("===本次gps时间小于等于上个点的gps时间，不予处理! (本次:{},上次：{}) 通信号：{} ===", curGpsTime, lastGpsTime, tid);
            return false;
        }

        //发动机转速不在合理区间内
        if (rotation < rotationMin || rotation > rotationMax) {
            logger.info("===发动机转速不在合理区间内，不予处理!（转速：" + rotation + "rpm）通信号：{} ===", tid);
            return false;
        }

        //速度不在合理区间内
        if (speed < speedMin || speed > speedMax) {
            logger.info("===速度不在合理区间内，不予处理!（速度：" + speed + "km/h）通信号：{} ===", tid);
            return false;
        }

        return true;
    }

    /*
     * 添加0200数据，累加停车通电时长
     * 累加停车通电时长只适用于流计算，离线计算中没有行程外的位置点
     * @param drivingData 中间变量
     * @param point       点信息
     */
    /*private void deal0200(DrivingData drivingData, Point0200 point) {
        //将0200相关指标放到drivingData的commonData中
        IntegrateSupportUtil.assign0200Data(drivingData ,point);

        //varTrip为null是行程外
        if (null == drivingData.getTripInfo()) {
            String objJson = null;
            try {
                objJson = JsonUtils.toJson(drivingData.getOuterStatistic().get(ParkingPowerOn.class.getSimpleName()));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }

            //累加停车通电时长
            if (objJson != null) {
                Long gpsTime = point.getGpsTime();
                VarParkingPowerOn varParkingPowerOn = JsonUtils.fromJson(objJson, VarParkingPowerOn.class);

                //0200的GPs时间大于上次行程结束时间
                if (null!=varParkingPowerOn  && gpsTime>varParkingPowerOn.getParkingTime()) {
                    //ACC开且上次ACC也为开
                    if (point.getAccStatus()==1 && varParkingPowerOn.isLastAccStatus() && point.getRotation() < rotation0200) {
                        //时间差大于0，停车通电时长累加
                        long time = gpsTime - varParkingPowerOn.getLastGpsTime();
                        if (time > 0) {
                            varParkingPowerOn.setParkingPowerOnTime((int) (varParkingPowerOn.getParkingPowerOnTime() + time));
                        }
                    }
                    //赋值
                    varParkingPowerOn.setLastGpsTime(gpsTime);
                    varParkingPowerOn.setLastAccStatus(point.getAccStatus()==1 && point.getRotation()<rotation0200);
                }
                drivingData.getOuterStatistic().put(ParkingPowerOn.class.getSimpleName(), varParkingPowerOn);
            }
        }
    }*/

    /**
     * 判断是否是缺项
     * @param engineData
     * @param missGetterName
     * @return
     */
    private boolean isMissTerm(EngineAggregatedData engineData, String missGetterName) {
        return judgeTerm(engineData, missGetterName);
    }

    /**
     * 判断是否异常
     * @param engineData
     * @param abnmGetterName
     * @return
     */
    private boolean isAbnmTerm(EngineAggregatedData engineData, String abnmGetterName) {
        return judgeTerm(engineData, abnmGetterName);
    }

    /**
     * 判断该项时长是否大于行程总时长34%
     * @param engineData
     * @param getterName
     * @return
     */
    private boolean judgeTerm(EngineAggregatedData engineData, String getterName) {
        try {
            int termDuration = (int) engineData
                    .getClass()
                    .getMethod(getterName, new Class[]{})
                    .invoke(engineData, new Object[]{});
            return engineData.getRouteDuration() == 0 ? false : 1.0 * termDuration / engineData.getRouteDuration() > 0.34;
        } catch (Exception e) {
            return false;
        }
    }
}
